#pragma once
#include "util.hpp"
#include <mutex>
#include <cassert>
#include <stdio.h>

// 对数据库中用户信息的管理模块（注册等）
class user_exec
{
private:
    MYSQL *_mysql;     // mysql句柄
    std::mutex _mutex; // 互斥锁，防止多个线程使用句柄
public:
    user_exec()
    {
    }
    user_exec(const std::string &host,
              const std::string &user,
              const std::string &passwd,
              const std::string &dbname,
              uint16_t port = 3306)
    {
        _mysql = mysql_util::mysql_create(host, user, passwd, dbname, port);
        assert(_mysql != NULL);
    }
    ~user_exec()
    {
        mysql_util::mysql_destroy(_mysql);
        _mysql = NULL;
    }
    // 用户的注册
    bool insert(Json::Value &user)
    {
#define INSERT_SQL "insert into users values (null, '%s',password('%s'), 1500,0,0);"
        // 防止用户名，密码冲突
        if (user["password"].isNull() || user["username"].isNull())
        {
            ELOG("INSERT USER INFO FAIL BECASE PASSWORD OR USERNAME");
            return false;
        }
        char sql[4096] = {0};
        // 格式化输出到sql数组中  sprintf(char *buffer,const char* format, ...)
        sprintf(sql, INSERT_SQL, user["username"].asCString(), user["password"].asCString());
        // 执行sql语句
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("insert into user info fail!");
            return false;
        }

        return true;
    }

    // 用户的登录验证，返回详细的用户信息
    bool login(Json::Value &user)
    {
#define SELECT_SQL "select id,score,total_count,win_count from users where username = '%s' and password = password('%s');"
        if (user["password"].isNull() || user["username"].isNull())
        {
            ELOG("INSERT USER INFO FAIL BECASE PASSWORD OR USERNAME");
            return false;
        }
        char sql[4096] = {0};
        sprintf(sql, SELECT_SQL, user["username"].asCString(), user["password"].asCString());
        MYSQL_RES *res = NULL;
        // 多个线程过来，保证线程安全(句柄操作和挑选信息的原子性)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("select user info fail!");
                return false;
            }

            // 挑选出来的信息，只能有一条，多了或者无都出错  保存查询结果到本地
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                DLOG("select user info is null");
                return false;
            }
        }

        // 获取一下信息的行数
        int n = mysql_num_rows(res);
        if (n != 1)
        {
            DLOG("select user info not unique");
            return false;
        }

        // 遍历保存的信息集
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    // 通过用户名获取用户信息
    bool select_by_name(const std::string &username, Json::Value &user)
    {
#define USER_NAME "select id,score,total_count,win_count from users where username = '%s';"
        char sql[4096] = {0};
        sprintf(sql, USER_NAME, username.c_str());
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("select user info by user_name fail!");
                return false;
            }

            // 挑选出来的信息，只能有一条，多了或者无都出错  保存查询结果到本地
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                DLOG("select user info by user_name is null");
                return false;
            }
        }

        // 获取一下信息的行数
        int n = mysql_num_rows(res);
        if (n != 1)
        {
            DLOG("select user info by user_name not unique");
            return false;
        }

        // 遍历保存的信息集
        MYSQL_ROW row = mysql_fetch_row(res);
        user["username"] = username;
        user["id"] = (Json::UInt64)std::stol(row[0]);
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    // 通过id获取用户信息
    bool select_by_id(uint64_t id, Json::Value &user)
    {
#define ID_SQL "select username,score,total_count,win_count from users where id = '%d';"
        char sql[4096] = {0};
        sprintf(sql, ID_SQL, id);
        MYSQL_RES *res = NULL;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false)
            {
                ELOG("select user info by id fail!");
                return false;
            }

            // 挑选出来的信息，只能有一条，多了或者无都出错  保存查询结果到本地
            res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                DLOG("select user info by id is null");
                return false;
            }
        }

        // 获取一下信息的行数
        int n = mysql_num_rows(res);
        if (n != 1)
        {
            DLOG("select user info by id not unique");
            return false;
        }

        // 遍历保存的信息集
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = (Json::UInt64)id;
        user["username"] = row[0];
        user["score"] = (Json::UInt64)std::stol(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);

        mysql_free_result(res);
        return true;
    }

    // 当win，积分增加100，wincount +1,total_count + 1
    bool win(uint64_t id)
    {
#define WIN_SQL "update users set score = score + 100, total_count = total_count +1,win_count = win_count +1 where id = '%d';"
        char sql[4096] = {0};
        sprintf(sql, WIN_SQL, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("update win exec fail!");
            return false;
        }

        return true;
    }

    // 当lose时，积分 - 100，total_count +1
    bool lose(uint64_t id)
    {
#define LOSE_SQL "update users set score = score - 100,total_count = total_count +1 where id = '%d';"
        char sql[4096] = {0};
        sprintf(sql, LOSE_SQL, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false)
        {
            ELOG("updata lose exec fail!");
            return false;
        }

        return true;
    }
};
